/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.pig4cloud.pigx.common.customize.security.utils;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.customize.contants.CommonFieldNameConstants;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.i18n.constants.I18nMessages;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 安全工具类
 *
 * @since 2022/11/10 14:32
 */
@Component
@AllArgsConstructor
public class SecurityUtil {

	private final CommonUtil commonUtil;

	/**
	 * 获取Authentication
	 */
	public Authentication getAuthentication() {
		return SecurityContextHolder.getContext().getAuthentication();
	}

	/**
	 * 获取用户
	 *
	 * @param authentication
	 * @return PigxUser
	 * <p>
	 */
	public PigxUser getUser(Authentication authentication) {
		Object principal = authentication.getPrincipal();
		if (principal instanceof PigxUser) {
			return (PigxUser) principal;
		}
		return null;
	}

	/**
	 * 获取用户
	 */
	public PigxUser getUser() {
		Authentication authentication = getAuthentication();
		return getUser(authentication);
	}

	/**
	 * 获取用户角色信息
	 *
	 * @return 角色集合
	 */
	public List<Long> getRoles() {
		Authentication authentication = getAuthentication();
		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

		List<Long> roleIds = new ArrayList<>();
		authorities.stream().filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE))
				.forEach(granted -> {
					String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE);
					roleIds.add(Long.parseLong(id));
				});
		return roleIds;
	}

	/**
	 * 获取当前用户
	 *
	 * @return {@link PigxUser}
	 * @author gong_da_kai
	 * @since 2021/11/20
	 * @since 1.0.0
	 */
	public static PigxUser getLoginUser() {
		if (hasLoginUser()) {
			return SecurityUtils.getUser();
		}

		return null;
	}

	/**
	 * 获取当前用户
	 *
	 * @return {@link PigxUser}
	 * @author gong_da_kai
	 * @since 2021/11/20
	 * @since 1.0.0
	 */
	public static PigxUser getLoginUserNotNull() {
		if (!hasLoginUser()) {
			throw new RuntimeException(MessageUtils.get(I18nMessages.UNABLE_TO_OBTATIN_USER_INFORMATION));
		}

		return SecurityUtils.getUser();
	}

	/**
	 * 判断当前是否有登录用户
	 *
	 * @return {@link boolean}
	 * @since 2022/8/22 21:09
	 */
	public static boolean hasLoginUser() {
		return !Objects.isNull(SecurityContextHolder.getContext().getAuthentication());
	}

	/**
	 * 初始化创建时数据
	 *
	 * @param parameter 对象
	 * @since 2022/8/14 16:36
	 */
	public void initCreateAbout(Object parameter) {
		initValue(parameter, CommonFieldNameConstants.INSERT_ABOUT_FIELD);
	}

	/**
	 * 初始化创建时数据
	 *
	 * @param user      当前用户
	 * @param now       当前时间
	 * @param parameter 对象
	 * @since 2022/8/14 16:36
	 */
	public void initCreateAbout(Object parameter, PigxUser user, LocalDateTime now) {
		initValue(parameter, CommonFieldNameConstants.INSERT_ABOUT_FIELD, user, now);
	}

	/**
	 * 初始化修改时数据
	 *
	 * @param parameter 对象
	 * @since 2022/8/14 16:36
	 */
	public void initUpdateAbout(Object parameter) {
		initValue(parameter, CommonFieldNameConstants.UPDATE_ABOUT_FIELD);
	}

	/**
	 * 初始化修改时数据
	 *
	 * @param parameter 对象
	 * @param user      当前用户
	 * @param now       当前时间
	 * @since 2022/8/14 16:36
	 */
	public void initUpdateAbout(Object parameter, PigxUser user, LocalDateTime now) {
		initValue(parameter, CommonFieldNameConstants.UPDATE_ABOUT_FIELD, user, now);
	}

	/**
	 * 初始化数据
	 *
	 * @param parameter     对象
	 * @param needInitField 需要初始化的数据
	 * @since 2022/8/14 16:36
	 */
	public void initValue(Object parameter, List<String> needInitField) {
		PigxUser user = getLoginUser();
		LocalDateTime now = LocalDateTime.now();
		initValue(parameter, needInitField, user, now);
	}

	/**
	 * 初始化数据
	 *
	 * @param parameter     对象
	 * @param needInitField 需要初始化的数据
	 * @param user          当前用户
	 * @param now           当前时间
	 * @since 2022/8/14 16:36
	 */
	public void initValue(Object parameter, List<String> needInitField, PigxUser user, LocalDateTime now) {
		Field[] fields = ReflectUtil.getFields(parameter.getClass());

		try {
			// 遍历
			for (Field field : fields) {
				boolean contains = needInitField.contains(field.getName());
				if (!contains) {
					continue;
				}

				if (!CommonFieldNameConstants.UPDATE_ABOUT_FIELD.contains(field.getName()) && !needFill(field, parameter)) {
					continue;
				}

				// 打破封装
				field.setAccessible(true);
				// 如果有值，不处理
				if (!Objects.isNull(field.get(parameter))) {
					field.setAccessible(false);
					continue;
				}

				// 根据属性名设置对应的值
				switch (field.getName()) {
					case CommonFieldNameConstants.ID:
						field.set(parameter, commonUtil.getId());
						break;

					case CommonFieldNameConstants.CREATE_BY:
					case CommonFieldNameConstants.UPDATE_BY:
						if (!Objects.isNull(user)) {
							field.set(parameter, user.getId());
						}

						break;

					case CommonFieldNameConstants.CREATE_TIME:
					case CommonFieldNameConstants.UPDATE_TIME:
						field.set(parameter, now);
						break;

					case CommonFieldNameConstants.STATUS:
						field.set(parameter, CommonDictEnum.Status.NORMAL.getValue());
						break;

					case CommonFieldNameConstants.IS_DELETED:
						field.set(parameter, CommonDictEnum.IsDeleted.NO.getValue());
						break;

					default:
				}

				// 重新封装
				field.setAccessible(false);
			}

		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 检查属性是否需要填充
	 *
	 * @param field     属性
	 * @param parameter 对象
	 * @return {@link boolean}
	 * @since 2022/8/14 16:48
	 */
	public boolean needFill(Field field, Object parameter) throws IllegalAccessException {
		field.setAccessible(true);
		Object value = field.get(parameter);
		field.setAccessible(false);

		// pigx框架额外校验，如果属性名是id
		if (extraNeedFill(field, value)) {
			return true;
		}

		return StringUtils.isEmpty(value);
	}

	/**
	 * pigx框架额外校验
	 *
	 * @param field 属性
	 * @param value 值
	 * @return {@link boolean}
	 * @since 2022/8/15 11:57
	 */
	private boolean extraNeedFill(Field field, Object value) {
		if (CommonFieldNameConstants.ID.equals(field.getName())) {
			return Objects.isNull(value) || "0".equals(value.toString());
		}

		return false;
	}
}
